package top.hmtools.jsCss.cssManager;

import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.w3c.css.sac.InputSource;
import org.w3c.dom.css.CSSRule;
import org.w3c.dom.css.CSSRuleList;
import org.w3c.dom.css.CSSStyleSheet;

import com.steadystate.css.parser.CSSOMParser;
import com.steadystate.css.parser.SACParserCSS3;

import top.hmtools.base.StringTools;
import top.hmtools.jsCss.autoConfiguration.JsCssAutoConfiguration;
import top.hmtools.jsCss.common.CommonTools;
import top.hmtools.jsCss.common.ResourcesBean;

/**
 * css管理者缺省实现
 * @author HyboJ
 * 创建日期：2017-9-27下午4:51:16
 */
@Component
//@ConditionalOnBean(IsEnableJsCss.class)
public class CssManagerDefault implements ICssManager {
	protected final Logger logger = LoggerFactory.getLogger(CssManagerDefault.class);

	/**
	 * css文件缓存库
	 */
	private static Map<String, ResourcesBean> CSS_REPERTORY = new HashMap<String, ResourcesBean>();
	
	@Autowired
	private JsCssAutoConfiguration jsCssAutoConfiguration;

	private String encoding  = "UTF-8";

	@Override
	@PostConstruct
	public void init() {
		// 初始化文件路径集合与文件编码格式，用于刷新
		String cssFilesPathStr = this.jsCssAutoConfiguration.getCssFilesPaths();
		this.logger.info("监控css的磁盘路径有：{}",cssFilesPathStr);
		String[] pathsArr = cssFilesPathStr.split(",");
		
		this.encoding = this.jsCssAutoConfiguration.getEncoding();
		String[] extensions = { "css","CSS"};
		
		//尝试加载当前运行工程classpath中的javascript文件
		for(String dir:pathsArr){
		    try {
		        Enumeration<URL> resources = CommonTools.getURLs(dir);
		        CommonTools.loadContent(encoding,dir, resources, CSS_REPERTORY,extensions);
		    } catch (IOException e1) {
		        this.logger.error("尝试从classpath中加载资源失败："+e1.getMessage(),e1);
		    }
		}
				
		this.logger.info("当前成功加载css文件总条数是：{}",CSS_REPERTORY.size());
	}

	@Override
	@PreDestroy
	public void destory() {
		if(CSS_REPERTORY != null){
			CSS_REPERTORY.clear();
		}
	}

	@Override
	public boolean refresh() {
		boolean result = false;
		try {
			this.destory();
			this.init();
			result=true;
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
		return result;
	}

	@Override
	public String getCss(String cssNames) {
		if(cssNames != null && !"".equals(cssNames)){
			String[] cssNamesArr = cssNames.split(",");
			return this.getCss(cssNamesArr);
		}else{
			return "\n\r";
		}
	}

	@Override
	public String getCss(List<String> cssNames) {
		if(cssNames != null && cssNames.size()>0){
			String[] cssNamesArr = cssNames.toArray(new String[0]);
			return this.getCss(cssNamesArr);
		}else{
			return "\n\r";
		}
	}

	@Override
	public String getCss(String[] cssNames) {
		StringBuffer sb_result = new StringBuffer("\n\r");
		if(cssNames != null && cssNames.length>0){
			for(String fileName:cssNames){
				String css_content_tmp = this.getOneCssContent(fileName);
				if(css_content_tmp == null || "".equals(css_content_tmp)){
					continue;
				}
				sb_result.append(css_content_tmp+"\n\r");
			}
			return sb_result.toString();
		}else{
			return "\n\r";
		}
	}
	
	/**
	 * 获取单个CSS文件内容
	 * @param keyWord
	 * @return
	 */
	private String getOneCssContent(String keyWord){
		String result = null;
		//检查入参
		if(keyWord == null || keyWord.trim().length()<=0){
			return result;
		}
		
		//key统一为小写字母
		keyWord = keyWord.trim().toLowerCase();
		
		//key统一使用“.css”后缀结尾
		if(!keyWord.endsWith(".css")){
			keyWord=keyWord+".css";
		}
		
		//获取仓库中所有索引名称
		List<String> filenames = this.listCssFilenames();
		
		//以后缀是否匹配为条件检索
		for(String filename:filenames){
			if(filename.endsWith(keyWord)){
				ResourcesBean resourcesBean = CSS_REPERTORY.get(filename);
				//TODO 处理CSS中URL中的相对路径
				String content = this.replaceRelativeURI(resourcesBean.getRootPath()+"/"+resourcesBean.getFileRelativeDir(), resourcesBean.getFileContent());
				
				result = "\r\n/**  file :: "+resourcesBean.getFileRelativePath()+" :: start **/"+content + "\r\n/**  file :: "+resourcesBean.getFileRelativePath()+" :: end **/";
			}
		}
		return result;
	}
	
	/**
	 * 将CSS中的相对Uri替换成绝对URL
	 * @param srcContent
	 * @return
	 */
	private String replaceRelativeURI(String relativePath,String cssContent){
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String path = this.getServerRootURL()+"/"+this.jsCssAutoConfiguration.getSrcUri()+"/"+relativePath;
		path = path.replaceAll("\\\\", "/");
		path = request.getScheme()+"://"+path.replaceAll("/+", "/");
		
		//解析CSS
		InputSource source = new InputSource(new StringReader(cssContent));
        CSSOMParser parser = new CSSOMParser(new SACParserCSS3());
        try {
			CSSStyleSheet sheet = parser.parseStyleSheet(source, null, null);
			CSSRuleList cssRules = sheet.getCssRules();
			
			for (int ii = 0; ii < cssRules.getLength(); ii++) {
	            CSSRule item = cssRules.item(ii);
	            String cssText = item.getCssText();
	            
	            // url\(\S*\) 筛选出含有 “url”的
	            String patternUrl = "url\\(\\S*\\)";
	            Pattern compileUrl = Pattern.compile(patternUrl);
	            Matcher matcherUrl = compileUrl.matcher(cssText);
	            while (matcherUrl.find()) {
	                // item.setCssText(".hm { background: #ffcc44; }");
	                String substring = cssText.substring(matcherUrl.start(),matcherUrl.end());
	                
	                //筛选出url中的纯路径字符串
	                String patternPath = "\\(['\"]?([^ \\f\\n\\r\\t\\v'\"]+)['\"]?\\)";
	                Pattern compilePath = Pattern.compile(patternPath);
	                Matcher matcherPath = compilePath.matcher(substring);
	                while(matcherPath.find()){
	                    String finalOldSRC = matcherPath.group(1);
	                    logger.debug("原始url：{}",finalOldSRC);
	                    try {
	                    	//验证url中的字符串是否是uri的绝对路径描述
							URI oldSrcUri = new URI(finalOldSRC);
							boolean isAbsolute = oldSrcUri.isAbsolute();
							if(isAbsolute){
								continue;
							}
						} catch (URISyntaxException ee) {
							this.logger.error("验证是否是绝对uri异常："+ee.getMessage(),ee);
						}
	                    
	                    //组装新的绝对URL
	                    URI base;
						try {
							base = new URI(path);//基本网页URI
							this.logger.debug("基本url:{}",base);
							URI abs=base.resolve(finalOldSRC);//解析于上述网页的相对URL，得到绝对URI
							this.logger.debug("资源uri：{}",abs);
							URL absURL=abs.toURL();//转成URL
							this.logger.debug("绝对url：{}",absURL);
							
							cssContent=cssContent.replace(finalOldSRC, absURL.toString());
						} catch (URISyntaxException e) {
							this.logger.error("css相对路径转换异常："+e.getMessage(),e);
						}
	                }
	            }
	        }
		} catch (IOException e) {
			this.logger.error("处理CSS中的URL相对路径异常："+e.getMessage(),e);
			return cssContent;
		}
        
		return cssContent;
	}
	
	/**
	 * 获取当前请求的根URL
	 * @return
	 */
	private String getServerRootURL() {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		
		//组装根URL
		StringBuffer sbTmp = new StringBuffer();
//		sbTmp.append(request.getScheme()+"://");// 协议
		sbTmp.append(request.getServerName());// 服务器地址
		int port = request.getServerPort();
		if (port != 80) {
			sbTmp.append(":" + port);
		}

		String contextPath = request.getContextPath(); // 项目名称
		if (StringTools.isNotBlank(contextPath)) {
			sbTmp.append("/"+contextPath);
		}
		return sbTmp.toString();
	}

	@Override
	public List<String> listCssFilenames() {
		List<String> result = new ArrayList<String>();
		if(CSS_REPERTORY != null){
			Set<String> keySet = CSS_REPERTORY.keySet();
			result.addAll(keySet);
			Collections.sort(result);
		}
		return result;
	}

	@Override
	public List<ResourcesBean> listResourcesBeans() {
		ArrayList<ResourcesBean> result = new ArrayList<ResourcesBean>();
		if(CSS_REPERTORY!=null){
			Collection<ResourcesBean> values = CSS_REPERTORY.values();
			result.addAll(values);
			//按基本文件名的字典排序
			Collections.sort(result, new Comparator<ResourcesBean>(){

				@Override
				public int compare(ResourcesBean aa, ResourcesBean bb) {
					String aaName = aa.getFileBaseName();
					String bbName = bb.getFileBaseName();
					if(StringTools.isAnyBlank(aaName,bbName)){
						return 0;
					}
					List<String> tmp = new ArrayList<>();
					tmp.add(aaName);
					tmp.add(bbName);
					Collections.sort(tmp);//字典序
					boolean isUnResorted = tmp.get(0).equals(aaName);
					if(isUnResorted){
						return -1;
					}else{
						return 1;
					}
				}
				
			});
		}
		return result;
	}

}
